package com.ddxcloud.common.aspect;

import cn.hutool.crypto.SecureUtil;
import com.ddxcloud.common.annotation.RepeatSubmit;
import com.ddxcloud.common.handler.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Objects;

@Aspect
@Slf4j
public class RepeatSubmitAspect {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    static final String REDIS_SUFFIX = "REPEAT_SUBMIT_URL:";

    public static HttpServletRequest getRequest() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return Objects.requireNonNull(attributes).getRequest();
    }

    @Pointcut("@annotation(com.ddxcloud.common.annotation.RepeatSubmit)")
    public void pointCut() {}

    @Around(value = "pointCut()", argNames = "joinPoint")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = getRequest();
        String redisKey = REDIS_SUFFIX + SecureUtil.md5(request.getRequestURL() + request.getLocalAddr());
        if (Boolean.TRUE.equals(redisTemplate.hasKey(redisKey))) {
            throw new BusinessException("操作过于频繁，请稍后再试");
        } else {
			MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
			Method method = methodSignature.getMethod();
			RepeatSubmit repeatSubmit = method.getAnnotation(RepeatSubmit.class);
            redisTemplate.opsForValue().set(redisKey, request.getLocalAddr(), repeatSubmit.lockTime(), repeatSubmit.timeUnit());
        }
        Object result;
        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
        return result;
    }
}
